//
//  CalculatorBrain.swift
//  myswift
//
//  Created by Harry on 15/6/14.
//  Copyright (c) 2015年 yangtuoxs. All rights reserved.
//

import Foundation

class CalculatorBrain {
    
    private enum Op: CustomStringConvertible {
        
        case Operand(Double)
        case UnaryOperation(String, Double -> Double)
        case BinaryOperation(String, (Double, Double) ->Double)
        
        var description: String {
              get {
                switch self{
                case .Operand( let oprand):
                    return "\(oprand)"
                case .UnaryOperation(let symbol , _):
                    return symbol
                case .BinaryOperation(let symbol, _):
                    return symbol
                }
              }
        }
    }
    
    private var opStack = [Op]( )
    
    private var knownOps = [String:Op]( )
     
    init( ) {
        knownOps["x"] = Op.BinaryOperation("x", * )
        knownOps["+"] = Op.BinaryOperation("+") { $0 + $1 }
        knownOps["-"] = Op.BinaryOperation("-") { $1 - $0 }
        knownOps["/"] = Op.BinaryOperation("/") { $1 / $0 }
    }
    
    private func evaluate(ops: [Op] ) ->(result: Double? , remainingOps: [Op])
    {
        if !ops.isEmpty {
            var remainingOps = ops
            let op = remainingOps.removeLast()
            switch op {
            case .Operand(let operand) :
                return (operand, remainingOps)
            case .UnaryOperation(_, let operation):
                let operandEvaluation = evaluate(remainingOps)
                if let operand = operandEvaluation.result {
                    return (operation(operand), operandEvaluation.remainingOps)
                }
            case .BinaryOperation(_, let operation):
                let op1Evaluation = evaluate(remainingOps)
                if let operand1 = op1Evaluation.result{
                    let op2Evaluation = evaluate(remainingOps)
                    if let operand2 = op2Evaluation.result{
                        return (operation(operand1,operand2), op2Evaluation.remainingOps)
                    }
                }
            }
        }
        return (nil, ops)
    }

    
    func evaluate() -> Double? {
        let (result, _) = evaluate(opStack)
        return result
    }
    
    func pushOperand(operand: Double){
        opStack.append(Op.Operand(operand))
    }
    
    func performOperation(symbol: String){
        if let operation = knownOps[symbol] {
            opStack.append(operation)
        }
    }
    
    
}